import { cloneDeep } from "lodash";
import { v4 as uuidV4 } from "uuid";
import {
  rootId,
  IChildItem,
  ICommonItem,
  IProject,
  IWorkspace,
  db,
  getTableName,
  DataSet,
} from "../model";
type errors = "ID_EXISTS" | "ID_NOT_EXISTS";
function error(name: errors, message?: string) {
  const err = new Error(message);
  err.name = name;
  throw err;
}
export class Service {
  static childItems: { [key: string]: IChildItem };
  static commonItems: { [key: string]: ICommonItem };
  static projects: { [key: string]: IProject };
  static workspaces: { [key: string]: IWorkspace };
  static readonly INBOX_ID = "inbox";
  public static load() {
    Service.childItems = db.get(getTableName("childitem"));
    Service.commonItems = db.get(getTableName("commonitem"));
    Service.projects = db.get(getTableName("project"));
    Service.workspaces = db.get(getTableName("workspace"));
    Service._addInbox();
    Service.save();
  }
  private static _addInbox() {
    if (!Service.projects.inbox)
      Service.addProject({ id: Service.INBOX_ID, title: "收件箱" });
  }
  public static clear() {
    Service.childItems = {};
    Service.commonItems = {};
    Service.projects = {};
    Service.workspaces = {};
    Service._addInbox();
    Service.save();
  }
  public static save() {
    db.set(getTableName("childitem"), Service.childItems);
    db.set(getTableName("commonitem"), Service.commonItems);
    db.set(getTableName("project"), Service.projects);
    db.set(getTableName("workspace"), Service.workspaces);
  }
  public static clean() {
    for (const key in Service.commonItems) {
      const commonItem = Service.commonItems[key];
      if (commonItem.childItemIds.length < 1) continue;
      commonItem.childItemIds.forEach((id, idx) => {
        if (!Service.childItems[id]) {
          commonItem.childItemIds.splice(idx, 1);
        }
      });
    }
    for (const key in Service.projects) {
      const project = Service.projects[key];
      if (project.commonItemIds.length < 1) continue;
      project.commonItemIds.forEach((id, idx) => {
        if (!Service.commonItems[id]) {
          project.commonItemIds.splice(idx, 1);
        }
      });
    }
    for (const key in Service.workspaces) {
      const workspace = Service.workspaces[key];
      if (workspace.projectIds.length < 1) continue;
      workspace.projectIds.forEach((id, idx) => {
        if (!Service.projects[id]) {
          workspace.projectIds.splice(idx, 1);
        }
      });
    }
    Service.save();
  }
  public static format() {
    for (const key in Service.projects) {
      const project = Service.projects[key];
      if (project.commonItemIds.length < 2) continue;
      const commonItemsList = cloneDeep(
        project.commonItemIds
          .map((id) => Service.commonItems[id])
          .filter((i) => !!i)
      );
      commonItemsList.sort((a, b) => a.index - b.index);
      commonItemsList.forEach((v, idx) => (v.index = idx));
      commonItemsList.forEach((v) => (Service.commonItems[v.id] = v));
      Service.save();
    }
  }
  public static listChildItems() {
    return Object.keys(Service.childItems).map((key) =>
      cloneDeep(Service.childItems[key])
    );
  }
  public static listCommonItems() {
    return Object.keys(Service.commonItems).map((key) =>
      cloneDeep(Service.commonItems[key])
    );
  }
  public static listProjects() {
    return Object.keys(Service.projects).map((key) =>
      cloneDeep(Service.projects[key])
    );
  }
  public static listWorkspaces() {
    return Object.keys(Service.workspaces).map((key) =>
      cloneDeep(Service.workspaces[key])
    );
  }
  public static addChildItem(options: { parent: string; title?: string }) {
    if (!Service.commonItems[options.parent]) error("ID_NOT_EXISTS");
    const id = uuidV4();
    const parent = options.parent;
    const title = options.title || "";
    Service.childItems[id] = {
      id,
      parent,
      done: false,
      itemType: "childitem",
      title,
    };
    Service.commonItems[parent].childItemIds.push(id);
    return cloneDeep(Service.childItems[id]);
  }

  public static addCommonItem(options: {
    parent: string;
    index?: number;
    title?: string;
    note?: string;
  }) {
    if (!Service.projects[options.parent]) error("ID_NOT_EXISTS");
    const id = uuidV4();
    const parent = options.parent;
    const title = options.title || "";
    const note = options.note || "";
    const index =
      options.index ||
      Service.listCommonItems().filter((i) => i.parent === parent).length;
    Service.commonItems[id] = {
      id,
      parent,
      done: false,
      itemType: "project",
      childItemIds: [],
      title,
      note,
      index,
    };
    if (parent !== rootId) Service.projects[parent].commonItemIds.push(id);
    Service.format();
    return cloneDeep(Service.commonItems[id]);
  }

  public static addProject(
    options: {
      id?: string;
      parent?: string;
      title?: string;
      note?: string;
    } = {}
  ) {
    if (options && options.parent && !Service.workspaces[options.parent])
      error("ID_NOT_EXISTS");
    const id = options?.id || uuidV4();
    const parent = options.parent || rootId;
    const title = options.title || "New Project";
    const note = options.note || "";
    Service.projects[id] = {
      id,
      parent,
      itemType: "project",
      commonItemIds: [],
      title,
      note,
    };
    if (parent !== rootId) Service.workspaces[parent].projectIds.push(id);
    return cloneDeep(Service.projects[id]);
  }
  public static addWorkspace(
    options: {
      id?: string;
      title?: string;
    } = {}
  ) {
    const id = options?.id || uuidV4();
    const parent = rootId;
    const title = options.title || "New Workspace";
    Service.workspaces[id] = {
      id,
      parent,
      itemType: "workspace",
      projectIds: [],
      title,
    };
    return cloneDeep(Service.workspaces[id]);
  }
  public static updateCommonItem(options: {
    id: string;
    done?: boolean;
    parent?: string;
    title?: string;
    note?: string;
    index?: number;
  }) {
    const commonItem = Service.commonItems[options.id];
    if (!commonItem) error("ID_NOT_EXISTS");
    if (options.parent && !Service.projects[options.parent])
      error("ID_NOT_EXISTS");
    if (typeof options.title !== "undefined") commonItem.title = options.title;
    if (typeof options.note !== "undefined") commonItem.note = options.note;
    if (typeof options.index !== "undefined") commonItem.index = options.index;
    if (typeof options.done !== "undefined") commonItem.done = options.done;
    if (typeof options.parent !== "undefined")
      commonItem.parent = options.parent;
    return commonItem;
  }
  public static removeChildItem(id: string) {
    const childItem = Service.childItems[id];
    if (!childItem) return;
    const childItemIds = Service.commonItems[childItem.parent].childItemIds;
    childItemIds.splice(childItemIds.indexOf(childItem.id), 1);
    delete Service.childItems[id];
    return childItem;
  }
  public static removeCommonItem(id: string) {
    const commonItem = Service.commonItems[id];
    if (!commonItem) return;
    const origin = {
      commonItem: cloneDeep(commonItem),
      childItems: commonItem.childItemIds.map((id) =>
        Service.removeChildItem(id)
      ),
    };
    const commonItemIds = Service.projects[commonItem.parent].commonItemIds;
    commonItemIds.splice(commonItemIds.indexOf(commonItem.id), 1);
    delete Service.commonItems[id];
    Service.format();
    return origin;
  }
  public static removeProject(id: string) {
    const project = Service.projects[id];
    if (!project) return;
    const origin = {
      project: cloneDeep(project),
      childItems: project.commonItemIds.map((id) =>
        Service.removeCommonItem(id)
      ),
    };
    if (project.parent !== rootId) {
      const projectIds = Service.workspaces[project.parent].projectIds;
      projectIds.splice(projectIds.indexOf(project.id), 1);
    }
    delete Service.projects[id];
    if (id === Service.INBOX_ID) Service._addInbox();
    return origin;
  }
  public static removeWorkspace(id: string) {
    const workspace = Service.workspaces[id];
    if (!workspace) return;
    const origin = {
      project: cloneDeep(workspace),
      childItems: workspace.projectIds.map((id) => Service.removeProject(id)),
    };
    delete Service.workspaces[id];
    return origin;
  }
  public static getChildItemByIds(ids: string[]) {
    return ids.map((id) => Service.childItems[id]);
  }
  public static getCommonItemByIds(ids: string[]) {
    return ids.map((id) => Service.commonItems[id]);
  }
  public static getProjectByIds(ids: string[]) {
    return ids.map((id) => Service.projects[id]);
  }
  public static getWorkspaceByIds(ids: string[]) {
    return ids.map((id) => Service.workspaces[id]);
  }
}
Service.load();
Service.format();
Service.clean();
